package com.duoduo.common.job;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;

import com.duoduo.common.job.properties.PowerJobProperties;
import com.duoduo.common.job.properties.PowerJobProperties.Worker;

import tech.powerjob.common.utils.CommonUtils;
import tech.powerjob.common.utils.NetUtils;
import tech.powerjob.worker.PowerJobSpringWorker;
import tech.powerjob.worker.common.PowerJobWorkerConfig;

/**
 * PowerJob自动装配
 * @author mason
 *
 */
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@EnableConfigurationProperties(PowerJobProperties.class)
public class PowerJobAutoConfiguration {
	
	/**
	 * 服务名称 包含 POWER_JOB_ADMIN 则说明是 Admin
	 */
	private static final String POWER_JOB_ADMIN = "power-job-admin";
	
	
	/**
	 * 配置Power-job 执行器，提供自动发现 Power-job-admin 能力
	 * @param PowerJobProperties Power 配置
	 * @param environment 环境变量
	 * @param discoveryClient 注册发现客户端
	 * @return
	 */
	@Bean
	public PowerJobSpringWorker powerJobSpringExecutor(PowerJobProperties powerJobProperties, Environment environment,
			DiscoveryClient discoveryClient) {
        /*
         * Create OhMyConfig object for setting properties.
         */
        PowerJobWorkerConfig config = new PowerJobWorkerConfig();
        
        Worker worker = powerJobProperties.getWorker();
		// 应用名默认为服务名
		String appName = worker.getAppName();
		if (!StringUtils.hasText(appName)) {
			appName = environment.getProperty("spring.application.name");
		}
		config.setAppName(appName);
		// 如果配置为空则获取注册中心的服务列表 "http://duo-power:9080/power-job-admin"
		if (!StringUtils.hasText(powerJobProperties.getWorker().getServerAddress())) {
			String serverList = discoveryClient.getServices()
				.stream()
				.filter(s -> s.contains(POWER_JOB_ADMIN))
				.flatMap(s -> discoveryClient.getInstances(s).stream())
				.map(instance -> String.format("http://%s:%s/%s", instance.getHost(), instance.getPort(),
						POWER_JOB_ADMIN))
				.collect(Collectors.joining(","));
			worker.setServerAddress(serverList);
		}
		
		 /*
         * Address of PowerJob-server node(s). Do not mistake for ActorSystem port. Do not add
         * any prefix, i.e. http://.
         */
        CommonUtils.requireNonNull(worker.getServerAddress(), "serverAddress can't be empty! " +
            "if you don't want to enable powerjob, please config program arguments: powerjob.worker.enabled=false");
        List<String> serverAddress = Arrays.asList(worker.getServerAddress().split(","));

        /*
         * Configuration of worker port. Random port is enabled when port is set with non-positive number.
         */
        if (worker.getPort() != null) {
            config.setPort(worker.getPort());
        } else {
        	int port = NetUtils.getRandomPort();
            config.setPort(port);
        }
        /*
         * appName, name of the application. Applications should be registered in advance to prevent
         * error. This property should be the same with what you entered for appName when getting
         * registered.
         */
        config.setAppName(worker.getAppName());
		config.setServerAddress(serverAddress);
        config.setProtocol(worker.getProtocol());
        /*
         * For non-Map/MapReduce tasks, {@code memory} is recommended for speeding up calculation.
         * Map/MapReduce tasks may produce batches of subtasks, which could lead to OutOfMemory
         * exception or error, {@code disk} should be applied.
         */
        config.setStoreStrategy(worker.getStoreStrategy());
        /*
         * When enabledTestMode is set as true, PowerJob-worker no longer connects to PowerJob-server
         * or validate appName.
         */
        config.setEnableTestMode(worker.isEnableTestMode());
        /*
         * Max length of appended workflow context . Appended workflow context value that is longer than the value will be ignored.
         */
        config.setMaxAppendedWfContextLength(worker.getMaxAppendedWfContextLength());

        config.setTag(worker.getTag());

        config.setMaxHeavyweightTaskNum(worker.getMaxHeavyweightTaskNum());

        config.setMaxLightweightTaskNum(worker.getMaxLightweightTaskNum());

        config.setHealthReportInterval(worker.getHealthReportInterval());
        /*
         * Create PowerJobSpringWorker object and set properties.
         */
        return new PowerJobSpringWorker(config);
	}
}
